﻿/*
 * Nix PBEM Game Engine.
 * Copyright (C) 2008, Lauris Bukšis-Haberkorns
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

using Nix.PBeM.Engine.DataTypes;
using Nix.PBeM.Engine.DataSources;
using Nix.PBeM.Engine.DataSources.FileReader;

namespace Nix.PBeM.Engine
{
	public class FileEngine : Engine
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="FileEngine"/> class.
		/// </summary>
		/// <param name="configFile">Configuration file.</param>
		public FileEngine(string configFile)
			: base(configFile)
		{
			if (Engine.Instance == null)
				Engine.Instance = this;
			else
				throw new EngineException("There is already engine instance.");
		}
		
		public override bool LoadData()
		{
            // If game configuration file could not be loaded than exit.
            if ( ! base.LoadData() )
                return false;

			Console.WriteLine();
			Console.WriteLine(" ================== Loading data =================");
			Console.WriteLine();

			// #################### Primary data ####################

			// ################### Secondary data ###################

			#region Items
			Console.WriteLine("Loading items...");
			try
			{
				IStaticSource src = new ItemFileSource(Path.Combine(this.GameDir, "items.rules"));
				src.Load();
				Console.WriteLine("done");
			}
			catch (Exception ex)
			{
				Console.WriteLine("FAILED: {0}", ex.Message);
			}
			#endregion

			#region Skills
			Console.WriteLine("Loading skills...");
			try
			{
				IStaticSource src = new SkillFileSource(Path.Combine(this.GameDir, "skills.rules"));
				src.Load();
				Console.WriteLine("done");
			}
			catch (Exception ex)
			{
				Console.WriteLine("FAILED: {0}", ex.Message);
			}

			// Fix skill dependencies
			this.FixateSkillDependancies();
			#endregion

			#region Races
			Console.WriteLine("Loading races...");
			try
			{
				IStaticSource src = new RaceFileSource(Path.Combine(this.GameDir, "races.rules"));
				src.Load();
				Console.WriteLine("done");
			}
			catch ( Exception ex )
			{
				Console.WriteLine("FAILED: {0}", ex.Message);
			}
			#endregion

			#region Terrains
			Console.WriteLine("Loading terrains...");
			try
			{
				IStaticSource src = new TerrainFileSource(Path.Combine(this.GameDir, "terrains.rules"));
				src.Load();
				Console.WriteLine("done");
			}
			catch ( Exception ex )
			{
				Console.WriteLine("FAILED: {0}", ex.Message);
			}
			#endregion
			
			#region Constructions
			Console.WriteLine("Loading constructions...");
			try
			{
				IStaticSource src = new ConstructionFileSource(Path.Combine(this.GameDir, "constructions.rules"));
				src.Load();
				Console.WriteLine("done");
			}
			catch ( Exception ex )
			{
				Console.WriteLine("FAILED: {0}", ex.Message);
			}
			#endregion

			// Static data is loaded, new items to it can not be added
			this.StaticDataIsLoaded = true;

			// #################### Dynamic data ####################
			
			#region Factions
			Console.WriteLine("Loading factions...");
			try
			{
				this.FactionsSrc = new FactionFileSource(Path.Combine(this.PlayersDir, "factions.dat"));
				this.FactionsSrc.Load();
				Console.WriteLine("done");
			}
			catch (Exception ex)
			{
				Console.WriteLine("FAILED: {0}", ex.Message);
			}
			#endregion

			#region Units
			Console.WriteLine("Loading units...");
			try
			{
				this.UnitsSrc = new UnitFileSource(Path.Combine(this.PlayersDir, "units.dat"));
				this.UnitsSrc.Load();
				Console.WriteLine("done");
			}
			catch ( Exception ex )
			{
				Console.WriteLine("FAILED: {0}", ex.Message);
			}
			#endregion

			#region Locations
			Console.WriteLine("Loading locations...");
			try
			{
				this.LocationsSrc = new LocationFileSource(Path.Combine(this.PlayersDir, "locations.dat"));
				this.LocationsSrc.Load();
				Console.WriteLine("done");
			}
			catch ( Exception ex )
			{
				Console.WriteLine("FAILED: {0}", ex.Message);
			}
			#endregion

			#region Structures
			Console.WriteLine("Loading structures...");
			try
			{
				this.StructuresSrc = new StructureFileSource(Path.Combine(this.PlayersDir, "structures.dat"));
				this.StructuresSrc.Load();
				Console.WriteLine("done");
			}
			catch ( Exception ex )
			{
				Console.WriteLine("FAILED: {0}", ex.Message);
			}
			#endregion

			this.DynamicDataIsLoaded = true;
			
			// ############# Check if all data is loaded ############

			#region Sanity check
			Console.WriteLine("Sanity check...");
			int warn = 0;

			#region Items
			// Check items
			foreach (Item item in this.items.Values)
			{
				if (!item.Loaded)
				{
					Console.WriteLine("Warning: Unknown item '{0}'", item.Tag);
					warn++;
				}
			}
			#endregion

			#region Skills
			// Check items
			foreach (Skill skill in this.skills.Values)
			{
				if ( ! skill.Loaded)
				{
					Console.WriteLine("Warning: Unknown skill '{0}'", skill.Tag);
					warn++;
				}
				foreach(SkillRequirement req in skill.SkillRequirements)
				{
					if ( ! req.Skill.HasSkillLevel(req.Level))
					{
						Console.WriteLine("Warning: Skill '{0}' has requirement for skill '{1}' level {2} that does not exist",
									skill.Tag, req.Skill.Tag, req.Level);
						warn++;
					}
				}
			}
			#endregion

			#region Races
			// Check items
			foreach ( Race race in this.races.Values )
			{
				if ( !race.Loaded )
				{
					Console.WriteLine("Warning: Unknown race '{0}'", race.Tag);
					warn++;
				}
			}
			#endregion

			#region Terrains
			// Check terrains
			foreach ( Terrain terrain in this.terrains.Values )
			{
				if ( !terrain.Loaded )
				{
					Console.WriteLine("Warning: Unknown terrain '{0}'", terrain.Tag);
					warn++;
				}
			}
			#endregion

			#region Constructions
			// Check constructions
			foreach ( Construction construction in this.constructions.Values )
			{
				if ( !construction.Loaded )
				{
					Console.WriteLine("Warning: Unknown construction '{0}'", construction.Tag);
					warn++;
				}
			}
			#endregion
			
			#region Factions
			// Check factions
			foreach (Faction f in this.factions.Values)
			{
				if ( ! f.Loaded)
				{
					Console.WriteLine("Warning: Referenced unknown faction '{0}'", f.Id);
					warn++;
				}
			}
			#endregion

			#region Units
			// Check units
			foreach ( Unit u in this.units.Values )
			{
				if ( !u.Loaded )
				{
					Console.WriteLine("Warning: Referenced unknown unit '{0}'", u.Id);
					warn++;
				}
				if ( u.Location == null)
				{
				    Console.WriteLine("Warning: Unit '{0}' is nowhere", u.Id);
				    warn++;
				}
			}
			#endregion

			#region Structures
			// Check structures
			foreach ( Structure s in this.structures.Values )
			{
				if ( ! s.Loaded )
				{
					Console.WriteLine("Warning: Referenced unknown structure '{0}'", s.Id);
					warn++;
				}
			}
			#endregion

			#region Locations
			// Check locations
			foreach ( Location l in this.locations.Values )
			{
				if ( !l.Loaded )
				{
					Console.WriteLine("Warning: Referenced unknown location '{0}'", l.Id);
					warn++;
				}
			}
			#endregion

			if ( warn == 0 )
			{
				Console.WriteLine("done");
				return true;
			}
			else
			{
				Console.WriteLine("FAILED: {0} warnings", warn);
				return false;
			}
			#endregion
		}
		
		public bool LoadOrder(Faction faction, bool syntaxCheck)
		{
            if ( ! Directory.Exists(Path.Combine(this.PlayersDir, faction.Tag)) )
            	return false;
			string orderFile = Path.Combine(Path.Combine(this.PlayersDir, faction.Tag),
                                            	"order." + this.TurnNumber.ToString() + ".txt");
            if ( ! File.Exists(orderFile) )
            	return false;
            
            OrderFileSource fs = new OrderFileSource(orderFile);
            fs.SyntaxCheck = syntaxCheck;
            try
            {
            	fs.Load();
            	return true;
            }
            catch (FileParserException ex)
            {
            	// TODO: Write warning about exception.
            	return false;
            }
		}
		
		public void LoadAllOrders()
		{
			foreach(Faction faction in this.factions.Values)
			{
				this.LoadOrder(faction, false);
			}
		}
	}
}
